import { blockTagNames,
  childTagNameMap,
  paraTagNames,
  baseParaTagName,
  autoCloseTagNames,
} from './config';
import { findPara, findBlock, findPreviousContent } from './find';
import { setStartAfterLastContent,
  setStartBeforeFirstContent,
  setEndBeforeFirstContent,
  setEndAfterLastContent
} from './range';

function isBlockNode(node) {
  if(!node || !(node instanceof Node)) {
    console.warn(`is block node: node-${node} is not a node, return false`);
    return false;
  }

  return blockTagNames.includes(node.tagName);
}

function isEmptyNode(node) {
  if(!node || (!(node instanceof Node) && !(node instanceof DocumentFragment))){
    console.error(`is empty node: ${node} is not a node`);
    return;
  }

  if(node.nodeType === Node.ELEMENT_NODE && autoCloseTagNames.includes(node.tagName)) {
    return false;
  } else if (node.nodeType === Node.ELEMENT_NODE && !node.childNodes.length) {
    return true;
  } else if (node.nodeType === Node.TEXT_NODE && node.nodeValue === '') {
    return true;
  } else if(node.nodeType === Node.ELEMENT_NODE) {
    let flag = true;
    node.childNodes.forEach(node => {
      if(!isEmptyNode(node)) flag = false;
    });
    return flag;
  } else if(node instanceof DocumentFragment) {
    if(!node.childNodes.length) return true;
    else {
      let flag = true;
      node.childNodes.forEach(node => {
        if(!isEmptyNode(node)) flag = false;;
      });
      return flag;
    }
  } else {
    return false;
  }
}

function isBlockFirstPara(node) {
  if(!node || !(node instanceof Node)) {
    console.warn(`is block first para: node-${node} is not a node, return false`);
    return false;
  }

  let block = findBlock(node);
  let para = findPara(node);

  return block.firstChild === para;
}

function isRangeNodeEnd(range, node) {
  if(!range || !node) return false;
  if(!(range instanceof Range) || !(node instanceof Node)) {
    console.warn(`isRangeNodeEnd: range-${range}, node-${node} type error`);
  }
  if(range.toString()) return false;
  let sc = range.startContainer, ec = range.endContainer;
  if(!isNodeSon(node, sc)) return false;
  if(!isNodeSon(node, ec)) return false;
  let newRange = range.cloneRange();
  newRange.setEndAfter(node);
  let content = newRange.cloneContents();
  return isEmptyNode(content);
}

function isNodeSon(parent, son) {
  if(!parent || !son) {
    console.warn(`isNodeSon: parnet-${parent}, son-${son}, return false`);
    return false;
  }
  if(!(parent instanceof Node) || !(son instanceof Node)) {
    console.warn(`isNodeSon: parnet-${parent}, son-${son}, return false`);
    return false;
  }
  if(son === parent) return true;
  else {
    let flag = false;
    parent.childNodes.forEach(node => {
      if(isNodeSon(node, son)) flag = true;
    })
    return flag;
  }
}

function ensureNewLine(node) {
  if(!node || !(node instanceof Node)) {
    console.error(`ensure new line: node-${node} is not a node, do nothing`);
    return;
  }

  if(!isBlockNode(node)) {
    console.error(`ensure new line: node-${node} is not a block, do nothing`);
    return;
  }

  if(!(node.lastChild instanceof Node) || !paraTagNames.includes(node.lastChild.tagName)) {
    let childTagName = childTagNameMap.get(node.tagName) || baseParaTagName;
    let newLine = document.createElement(childTagName);
    node.appendChild(newLine);
    newLine.appendChild(document.createTextNode(''));
  }
}

function ensureContent(node) {
  if(!node || !(node instanceof Node)) {
    console.error(`ensure content: node-${node} is not a node, do nothing`);
    return;
  }

  if(node.nodeType === Node.TEXT_NODE) return;
  if(isEmptyNode(node)) {
    let firstLeaf = node;
    while(firstLeaf.firstChild) firstLeaf = firstLeaf.firstChild;
    firstLeaf.appendChild(document.createTextNode(''));
  }
}

function ensureSelectContent(range) {
  let r = range;
  let sc = r.startContainer, ec = r.endContainer,
    so = r.startOffset, eo = r.endOffset;

  ensureContent(sc);
  ensureContent(ec);

  if(sc.nodeType !== Node.TEXT_NODE) {
    if(so >= sc.childNodes.length) {
      setStartAfterLastContent(range, sc);
    }
    else {
      setStartBeforeFirstContent(range, sc.childNodes[so]);
    }
  }

  if(ec.nodeType !== Node.TEXT_NODE) {
    if(eo >= ec.childNodes.length) {
      setEndAfterLastContent(range, ec);
    }
    else {
      setEndBeforeFirstContent(range, ec.childNodes[eo]);
    }
  }
}

function ensureSelectPreContent(range) {
  let r = range;
  let sc = r.startContainer, ec = r.endContainer,
    so = r.startOffset, eo = r.endOffset,
    sp = findPara(sc), ep = findPara(ec);
  
  let sPreText = findPreviousContent(sc);
  let ePreText = findPreviousContent(ec);

  if(sc.nodeType === Node.TEXT_NODE && sPreText !== sc && so === 0) {
    setStartAfterLastContent(range, sPreText);
  }

  if(ec.nodeType === Node.TEXT_NODE && ePreText !== ec && eo === 0) {
    setEndAfterLastContent(range, ePreText);
  }
}

export {
  isBlockNode,
  isEmptyNode,
  isBlockFirstPara,
  isRangeNodeEnd,
  ensureNewLine,
  ensureContent,

  ensureSelectContent,
  ensureSelectPreContent
}